﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO.Packaging;
using System.Linq;
using System.Net.Mime;
using System.Text;
using System.Xml.Linq;
using System.Text.RegularExpressions;
using System.IO;


namespace ReporterEngine
{
    /// <summary>
    /// model
    /// current model
    /// design element 
    /// 
    /// iterate all "p" tagst - lines
    /// </summary>
    public class WordReportEngine : ReportEngine<CIFModel,sQuery,XElement>
    {
        public override void Report(ReportEngine<CIFModel, sQuery, XElement>.ReportArgs args)
        {
            var rootNode = Template.Parse(args.CurrentDesign);

            while (rootNode.Root.Childs.Count > 0)
            {
                var currentCommand = rootNode.Root.Childs[0];

                this.ReportSettings.AddMessageLog(currentCommand.GetLog());

                switch (currentCommand.CommandType)
                {
                    case CommandTypes.Section:
                        DoSectionCommand(args, currentCommand);
                        break;
                    case CommandTypes.List:
                        DoListCommand(args, currentCommand);
                        break;
                    case CommandTypes.Text:
                        DoTextCommand(args, currentCommand);
                        break;
                    case CommandTypes.Diagram:
                        DoDiagramCommand(args, currentCommand);
                        break;
                    case CommandTypes.NextRow:
                    case CommandTypes.Row:
                        DoRowCommand(args, currentCommand);
                        break;
                    case CommandTypes.EndSection:
                        DoMissingStartSectionCommand(args, currentCommand);
                        break;
                    default:
                        DoUndefinedCommand(args, currentCommand);
                        break;
                }

                rootNode = Template.Parse(args.CurrentDesign);
            }
        }

        private void DoUndefinedCommand(ReportArgs args, TemplateNode currentCommand)
        {
            var actualPath = LexPath.Scan(currentCommand.Command.Query);

            if(actualPath.HasError)
            {
                //show error in document and replace by text only
                InvalidPathTextReplace(currentCommand, actualPath, args);

                return;
            }

            //  var resultText = "*****" + currentCommand.Command .CommandName + ":"+ currentCommand.Command.Query + "*****";

            string resultText = args.CurrentDesign.Value.Substring(currentCommand.StartPosition + 1, currentCommand.EndPosition - currentCommand.StartPosition - 1);
            resultText = "** Undefined command ... " + resultText + "**";

            SplitTextNodesIntoFirstAndReplace(args.CurrentDesign, currentCommand.StartPosition, currentCommand.EndPosition + 1, resultText, false, "red");
        }

        private void DoMissingStartSectionCommand(ReportArgs args, TemplateNode currentCommand)
        {
            var resultText = "** Missing start section **";

            SplitTextNodesIntoFirstAndReplace(args.CurrentDesign, currentCommand.StartPosition, currentCommand.EndPosition + 1, resultText, false, "red");
        }

        private void InvalidPathTextReplace(TemplateNode currentCommand, LexPath actualPath, ReportArgs args)
        {
            var errorpos = actualPath.LastIndex;
            var validPath = currentCommand.Command.Query.Substring(0, errorpos);
            var invalidPath = currentCommand.Command.Query.Substring(errorpos);

            var resErrorText = "**Invalid path in command " + validPath + "... on position ..." + invalidPath + "**";

            SplitTextNodesIntoFirstAndReplace(args.CurrentDesign, currentCommand.StartPosition, currentCommand.EndPosition + 1, resErrorText, false, "red");
        }

        private void DoTextCommand(ReportArgs args, TemplateNode currentCommand)
        {
            var actualPath = LexPath.Scan(currentCommand.Command.Query);

            if (actualPath.HasError)
            {
                //show error in document and replace by text only
                InvalidPathTextReplace(currentCommand, actualPath, args);

                return;
            }

            Interpreter interpreter = Interpreter.Create(actualPath);

            //sQuery query = sQuery.New(this.Model);
            //query.Elements.Add(args.CurrentData);
            sQuery query = args.CurrentData;

            var resultData = interpreter.Execute(actualPath, query);

            if (interpreter.HasError)
            {
                //show error
                ShowInterpreterError(currentCommand, args, interpreter);

                return;
            }

            var resultValue = resultData.Value;

            resultValue = resultValue
               .Replace("&amp;", "&")
               .Replace("&lt;", "<")
               .Replace("&gt;", ">")
               .Replace("&quot;", "\"")
               .Replace("&apos;", "'")

               .Replace("amp;", "&")
               .Replace("lt;", "<")
               .Replace("gt;", ">")
               .Replace("quot;", "\"")
               .Replace("apos;", "'");

            //if (resultValue.Contains("apos"))
            //{
            //    int ee = 12;
            //}

            this.ReportSettings.AddDebugMessageLog(@"command : {0},
Query : {1},
Results Count : {2},
Values Count : {3},
Value : {4},
Source Element : {5}"
                , "text", currentCommand.Command.Query, resultData.Elements.Count, resultData.Values.Count, resultValue,
                args.CurrentData.ToString());

        

            SplitTextNodesIntoFirstAndReplace(args.CurrentDesign, currentCommand.StartPosition, currentCommand.EndPosition + 1, resultValue, false);
        }

        List<ImagePart> ImageParts = new List<ImagePart>();

        private void DoDiagramCommand(ReportArgs args, TemplateNode currentCommand)
        {
            var actualPath = LexPath.Scan(currentCommand.Command.Query);
            Interpreter interpreter = Interpreter.Create(actualPath);

            //sQuery query = sQuery.New(this.Model);
            //query.Elements.Add(args.CurrentData);
            sQuery query = args.CurrentData;

            var resultData = interpreter.Execute(actualPath, query);

            if (interpreter.HasError && currentCommand.Command.Query.Trim().Length > 2)
            {
                //show error
                ShowInterpreterError(currentCommand, args, interpreter);

                return;
            }

            this.ReportSettings.AddDebugMessageLog(
                @"command : {0},
Query : {1},
Results Count : {2},
Values Count : {3},
Source Element : {4}"
                , "diagram", currentCommand.Command.Query, resultData.Elements.Count, resultData.Values.Count,
                args.CurrentData.ToString());

            //SplitTextNodesIntoFirstAndReplace(args.CurrentDesign, currentCommand.StartPosition,
            //                                  currentCommand.EndPosition + 1,
            //                                  "diagram : " + currentCommand.Command.Query, false);

            var startEl = GetElementByNode(args.CurrentDesign, currentCommand);
            var pEl = ClosestParent(startEl, x => x.Name == p);


            //var tempNextTT = pEl.ElementsAfterSelf().ToList();

            SplitTextNodesIntoFirstAndReplace(args.CurrentDesign, currentCommand.StartPosition,
                                           currentCommand.EndPosition + 1,
                                           "", false);

            //tempNextTT = pEl.ElementsAfterSelf().ToList();


           if(resultData.Elements.Count > 0 && resultData.Elements[0].Element("image") != null)
           {
               var filename = resultData.Elements[0].Element("image").Value;

               if (!string.IsNullOrEmpty(filename))
               {
                   var fi = new FileInfo(filename);

                   var fileName = fi.Name;




                   //add image
                   var ip = AddImagePart(fileName);

                   if(ip == null)
                   {
                       ReportSettings.AddMessageLog(new MessageLogArgs { Message = "Image does not exists for instance '" + this.Model.GetName(args.CurrentData.Element) + "' object id:" + this.Model.GetID(args.CurrentData.Element), MessageLogType = MessageLogTypes.Warning });

                       return;
                   }

                  

                   //goto nearest image and replace id
                   var xel = pEl;
                   var nearestimage = xel;

                   if (xel.Descendants(blip).Count() == 0)
                       nearestimage = xel.ElementsAfterSelf().FirstOrDefault(x => x.Descendants(blip).Count() > 0);



                   if (nearestimage == null && xel.Parent != null)
                   {
                       var tempNext = xel.ElementsAfterSelf().ToList();

                       //********** nearest image is missing
                       //ReportEngine.LogBuilder.AppendLine("nearest image is missing in document");

                       //ReportEngine.LogBuilder.AppendLine(command.Parameters);

                   }

                   XElement imageBlip = null;

                   if (nearestimage != null)
                   {
                       imageBlip = nearestimage
                           .Descendants(blip).FirstOrDefault();
                   }


                   if (imageBlip != null)
                   {
                       imageBlip.Attribute(attribute_embed).Value = ip.ID;

                       //set ratio for image
                       //get graph object
                       var sizesEl = nearestimage
                           .Descendants()
                           .Where(x => x.Attribute("cx") != null).First();

                       var cx = int.Parse(sizesEl.Attribute("cx").Value);
                       var cy = int.Parse(sizesEl.Attribute("cy").Value);

                       //                               imageWidthEMU =
                       //                                     (long)(
                       //                                     (imageFile.Width / imageFile.HorizontalResolution) * 914400L);
                       //                               imageHeightEMU =
                       //                                     (long)(
                       //                                     (imageFile.Height / imageFile.VerticalResolution) * 914400L);

                       if ((cx > ip.X*9525) && (cy*2 > ip.Y*9525))
                       {
                           cx = ip.X*9525;
                           cy = ip.Y*9525;
                       }
                       else
                       {
                           if (ip.X > ip.Y && cx > cy)
                           {
                               var pomer = (1.0*ip.Y)/(1.0*ip.X);
                               cy = (int) ((1.0*cx)*pomer);
                           }
                           else
                           {
                               var pomer = (1.0*ip.X)/(1.0*ip.Y);
                               cx = (int) ((1.0*cy)*pomer);
                           }
                       }

                       //update new values
                       foreach (var cxel in nearestimage
                           .Descendants()
                           .Where(x => x.Attribute("cx") != null))
                       {
                           cxel.Attribute("cx").Value = cx.ToString();
                           cxel.Attribute("cy").Value = cy.ToString();
                       }

                   }
               }
               else
               {
                   //file iimage is missing
               }
           }
           else
           {
               //diagram does not exists for this object
               ReportSettings.AddMessageLog(new MessageLogArgs { Message = "Diagram does not exists on '" + this.Model.GetName(args.CurrentData.Element) + "' object id:" + this.Model.GetID(args.CurrentData.Element), MessageLogType = MessageLogTypes.Warning });
           }

            //set diagram file
        }

        private void DoListCommand(ReportArgs args, TemplateNode currentCommand)
        {
            var startEl = GetElementByNode(args.CurrentDesign, currentCommand);

            var pEl = ClosestParent(startEl, x => x.Name == p);


            

            //sQuery query = sQuery.New(this.Model);
            //query.Elements.Add(args.CurrentData);
            sQuery query = args.CurrentData;

            var actualPath = LexPath.Scan(currentCommand.Command.Query);

            Interpreter interpreter = Interpreter.Create(actualPath);

            var resultData = interpreter.Execute(actualPath, query);

            if (interpreter.HasError)
            {
                //show error
                ShowInterpreterError(currentCommand, args, interpreter);

                return;
            }
            //replace command after verification
            SplitTextNodesIntoFirstAndReplace(args.CurrentDesign, currentCommand.StartPosition, currentCommand.EndPosition + 1, "", false);

            this.ReportSettings.AddDebugMessageLog(
                            @"command : {0},
Query : {1},
Results Count : {2},
Source Element : {3}"
                            , "List", currentCommand.Command.Query, resultData.Elements.Count, 
                            args.CurrentData.ToString());

            //SplitTextNodesIntoFirstAndReplace(args.CurrentDesign, currentCommand.StartPosition,
            //                                  currentCommand.EndPosition + 1,
            //                                  "diagram : " + currentCommand.Command.Query, false);



            foreach (var elData in resultData.Elements)
            {
                var tempWord = CreateTemporaryWord();

                tempWord.Root.Add(XElement.Parse(pEl.ToString()));

                var targs = WordReportEngine.CreateArgs();
                targs.CurrentDesign = tempWord.Root;
                targs.CurrentData = query.NewFromElement(elData);

                this.Report(targs);

                pEl.AddAfterSelf(tempWord.Root.Elements());
            }

            //if pcount > 0 &&
            if (resultData.Elements.Count > 0 && pEl.Parent.Name!= tc)
                pEl.Remove();
            else
            {
                //chceck last one p inside tc and dont remove
                if(pEl.ElementsAfterSelf().Count() == 0)
                    pEl.Elements().Remove();
                else
                {
                    pEl.Remove();
                }
            }
        }

        private void DoRowCommand(ReportArgs args, TemplateNode currentCommand)
        {
            var startEl = GetElementByNode(args.CurrentDesign, currentCommand);

            var trEl = ClosestParent(startEl, x => x.Name == tr);
            var rootEl = ClosestParent(startEl, x => x.Name == tr);
            

            if (currentCommand.CommandType == CommandTypes.NextRow)
                trEl = trEl.ElementsAfterSelf().First();
            //find next rows if contains merge effect
            //var nextRows = new List<XElement>();
            //var sourceRow = trEl.ElementsAfterSelf().FirstOrDefault();

            //while (sourceRow != null && sourceRow.Descendants().Where(x => x.Name == vMerge).Count() > 0)
            //{
            //    nextRows.Add(sourceRow);

            //    sourceRow = sourceRow.ElementsAfterSelf().FirstOrDefault();
            //}

            //find next rows if contains merge effect
            var nextRows = trEl.ElementsAfterSelf().TakeWhile(y => y.Descendants().Where(x => x.Name == vMerge).Count() > 0).ToList();

            

            //sQuery query = sQuery.New(this.Model);
            //query.Elements.Add(args.CurrentData);
            sQuery query = args.CurrentData;

            var actualPath = LexPath.Scan(currentCommand.Command.Query);

            Interpreter interpreter = Interpreter.Create(actualPath);

            var resultData = interpreter.Execute(actualPath, query);

            if (interpreter.HasError)
            {
                //show error
                ShowInterpreterError(currentCommand, args, interpreter);

                return;
            }

            //replace command after verification
            SplitTextNodesIntoFirstAndReplace(args.CurrentDesign, currentCommand.StartPosition, currentCommand.EndPosition + 1, "", false);

            this.ReportSettings.AddDebugMessageLog(@"command : {0},
Query : {1},
Results Count : {2},
Values Count : {3},
Source Element : {4}"
             , "row", currentCommand.Command.Query, resultData.Elements.Count, resultData.Values.Count,
             args.CurrentData.ToString());


            foreach (var elData in resultData.Elements)
            {
                var tempWord = CreateTemporaryWord();
                //skip first row if its not row command type
               
                tempWord.Root.Add(XElement.Parse(trEl.ToString()));


                tempWord.Root.Add(nextRows);
                var targs = WordReportEngine.CreateArgs();
                targs.CurrentDesign = tempWord.Root;
                targs.CurrentData = query.NewFromElement(elData);

                //if(query._model.GetID(elData) == 1306741)
                //{
                //    int eei = 12;
                //}

                var curid = query._model.GetID(elData);

                //if (curid == 1301111)
                //{
                //    int ee = 12;
                //}

                this.Report(targs);

                trEl.AddAfterSelf(tempWord.Root.Elements());

                //if(tempWord.Root.Descendants().Where(tcx=>tcx.Name == tc && (tcx.Descendants().Where(x=>x.Name == p).Count() == 0)).Count() > 0)
                //{
                //    int ee = 12;
                //}
            }

            //remove root row
            if (currentCommand.CommandType == CommandTypes.NextRow)
            {
                rootEl.Remove();
            }
            
            trEl.Remove();
            nextRows.ForEach(x=>x.Remove());
        }

        private void DoSectionCommand(ReportArgs args, TemplateNode currentCommand)
        {
            if(currentCommand.EndSectionNode == null)
            {
                string resultText = args.CurrentDesign.Value.Substring(currentCommand.StartPosition + 1, currentCommand.EndPosition - currentCommand.StartPosition - 1);
                resultText = "** Missing end section ... " + resultText + "**";

                SplitTextNodesIntoFirstAndReplace(args.CurrentDesign, currentCommand.StartPosition, currentCommand.EndPosition + 1, resultText, false, "red");

                return;
            }
            
            var startEl = GetElementByNode(args.CurrentDesign, currentCommand);
            var endEl = GetElementByNode(args.CurrentDesign, currentCommand.EndSectionNode);

            var startP = ClosestParent(startEl, x => x.Name == p);
            var endP = ClosestParent(endEl, x => x.Name == p);


           


            List<XElement> innerEls = ElementsAfterSelf(startP, endP);

            //sQuery query = sQuery.New(this.Model);
            //query.Elements.Add(args.CurrentData);
            sQuery query = args.CurrentData;

            var actualPath = LexPath.Scan(currentCommand.Command.Query);

            if(currentCommand.Command.Query == ">>members()>>(‘Business Process‘)<<subject>>(‘Workflow Model‘)")
            {
                int ee = 12;
            }

            Interpreter interpreter = Interpreter.Create(actualPath);

            var resultData = interpreter.Execute(actualPath, query);

            if(interpreter.HasError)
            {
                //show error
                ShowInterpreterError(currentCommand, args, interpreter);

                return;
            }

            if(resultData.HasError)
            {
                ShowQueryError(currentCommand, args, resultData);

                return;
            }

            //replace command after error verification

            //replace command and endsection command
            //end section first to prevent move of positions
            var endSectionstartEl = GetElementByNode(args.CurrentDesign, currentCommand.EndSectionNode);
            var endSectionP = ClosestParent(endSectionstartEl, x => x.Name == p);

            SplitTextNodesIntoFirstAndReplace(args.CurrentDesign, currentCommand.EndSectionNode.StartPosition, currentCommand.EndSectionNode.EndPosition + 1, "", false);

            if (string.IsNullOrEmpty(endSectionP.Value))
                endSectionP.Remove();

            SplitTextNodesIntoFirstAndReplace(args.CurrentDesign, currentCommand.StartPosition, currentCommand.EndPosition + 1, "", false);


            if (currentCommand.Command.Query.Contains("<<"))// && resultData.Elements.Count > 0)
            {
                int ee1 = 12;
            }

            this.ReportSettings.AddDebugMessageLog(@"command : {0},
Query : {1},
Results Count : {2},
Values Count : {3},
Source Element : {4}"
            , "section", currentCommand.Command.Query, resultData.Elements.Count, resultData.Values.Count,
            args.CurrentData.ToString());
            
            
            //create template for repeating document
            var tempWordTemplate = CreateTemporaryWord();

            List<XElement> newList = new List<XElement>();

            foreach (var newel in innerEls)
                newList.Add(XElement.Parse(newel.ToString()));

            //merge nodes into one to correct use of validation and generation, start section should be in diferent nodes
            tempWordTemplate.Root.Add(newList);

            foreach (var elData in resultData.Elements)
            {
                //var idtt = query._model.GetID(elData);
                //var idstr = query._model.GetElemenStringtID(elData);
                //if(idtt == 2264504)
                //{
                //    int eedt = 12;
                //}



                //clone elements
                //List<XElement> newList = new List<XElement>();

                //foreach (var newel in innerEls)
                //    newList.Add(XElement.Parse(newel.ToString()));
               
                ////merge nodes into one to correct use of validation and generation, start section should be in diferent nodes
                //var tempWord = CreateTemporaryWord();
                //tempWord.Root.Add(newList);
                var tempWord = XDocument.Parse(tempWordTemplate.ToString());
                //run engine
                var targs = WordReportEngine.CreateArgs();
                targs.CurrentDesign = tempWord.Root;
                targs.CurrentData = query.NewFromElement(elData);

                this.Report(targs);
                
                //append list after start section command
                //dont add nodes but report before and report after
                //!!!!!!!!
                startP.AddAfterSelf(tempWord.Root.Elements());


                int ee = 12;

            }

            //remove empty startr and endr, to prevent invisible breaks
            if (startP.Value.Length == 0 || startP.Value == "startp")
               startP.Remove();

            //delete innerEls after generate data
            innerEls.ForEach(x=>x.Remove());
        }

        private void ShowInterpreterError(TemplateNode currentCommand, ReportArgs args, Interpreter interpreter)
        {
            var errorpos = interpreter.ErrorToken.StartPosition;
            var validPath = currentCommand.Command.Query.Substring(0, errorpos);
            var invalidPath = currentCommand.Command.Query.Substring(errorpos);

            var resErrorText = "**Invalid path in command " + validPath + "... on position ..." + invalidPath + "**";

            SplitTextNodesIntoFirstAndReplace(args.CurrentDesign, currentCommand.StartPosition, currentCommand.EndPosition, resErrorText, false, "red");
        }

        private void ShowQueryError(TemplateNode currentCommand, ReportArgs args, sQuery query)
        {
            //var errorpos = interpreter.ErrorToken.StartPosition;
           // var validPath = currentCommand.Command.Query.Substring(0, errorpos);
            //var invalidPath = currentCommand.Command.Query.Substring(errorpos);

            var resErrorText = query.ErrorMessage.ToString();
            var message = resErrorText;
            message += "\t\tin command " + currentCommand.Command.Query;

            ReportSettings.AddMessageLog(new MessageLogArgs { Message = message, MessageLogType = MessageLogTypes.Error });

            SplitTextNodesIntoFirstAndReplace(args.CurrentDesign, currentCommand.StartPosition, currentCommand.EndPosition, resErrorText, false, "red");
        }

        public static XElement GetElementByNode(XElement root, TemplateNode node)
        {
            return GetElementByTextIndex(root, node.StartPosition + 1);
        }

        public static XElement GetElementByTextIndex(XElement element, int position)
        {
            int resInt = 0;

            return GetElementByTextIndex(element, position, out resInt);
        }

        public static XElement GetElementByTextIndex(XElement element, int position, out int valueStartPosition)
        {
            XElement result = null;

            int curPos = 0;
            valueStartPosition = curPos;
            
            foreach(var curEl in element.Descendants())
            {
                if (curEl.Elements().Count() == 0)
                {
                    curPos += curEl.Value.Length;
                }

                if(curPos > position)
                {
                    
                    return curEl;
                }

                valueStartPosition = curPos;
            }
            
            return result;
        }

        public static int GetStartPosition(XElement element, XElement findElement)
        {
            var curPos = 0;
            //XElement curElement = 0;

            foreach (var curEl in element.Descendants())
            {
                if (curEl.Elements().Count() == 0)
                {
                    curPos += curEl.Value.Length;
                }

                if (curEl == findElement)
                {

                    return curPos;
                }
            }
            
            return -1;
        }


        public void ReplaceCommand(TemplateNode tplNode, string value, XElement root)
        {
            var xel = GetElementByTextIndex(root, tplNode.StartPosition);

            xel.Value = value;
        }

        public static XElement ClosestParent(XElement xel, Func<XElement, bool> exp)
        {
            var parent = xel;

            while (parent != null && !exp(parent))
            {
                parent = parent.Parent;
            }

            return parent;
        }

        public static List<XElement> ElementsAfterSelf(XElement start, XElement end)
        {
            return start.ElementsAfterSelf().TakeWhile(x => x != end).ToList();

            List<XElement> innerEls = new List<XElement>();
            bool isBetween = true;

            foreach (var nextEl in start.ElementsAfterSelf())
            {
                if (nextEl == end)
                    isBetween = false;

                if (isBetween)
                    innerEls.Add(nextEl);
            }

            return innerEls;
        }

        //split text regions from to positions
        //split text over paragraphs sections
        public static void SplitTextNodesIntoFirst(XElement element, int start, int end, bool dontRemoveP)
        {
            var textToReplace = element.Value.Substring(start, end - start);

            SplitTextNodesIntoFirstAndReplace(element, start, end, textToReplace, dontRemoveP);
        }

        public static void SplitTextNodesIntoFirstAndReplace(XElement element, int start, int end, string textToReplace, bool dontRemoveP)
        {
            SplitTextNodesIntoFirstAndReplace(element, start, end, textToReplace, dontRemoveP, null);
        }
        
        public static void SplitTextNodesIntoFirstAndReplace(XElement element, int start, int end, string textToReplace, bool dontRemoveP, string highlightColor)
        {
            //debug onlyb 

         
            
            //if contains more rows, replace first text line only, and add new lines
            var isMultipleLines = textToReplace.Contains("\n");
            
            int startValuePosition = 0;
            int endValuePosition = 0;
            var startEl = GetElementByTextIndex(element, start, out startValuePosition);
            var endEl = GetElementByTextIndex(element, end - 1, out endValuePosition); //end position not length when 0 is first position

            var testContent = element.Value;
            var testLength = testContent.Length;

            if(endEl == null)
                endEl = GetElementByTextIndex(element, end - 1, out endValuePosition); //end position not length when 0 is first position

            var startP = ClosestParent(startEl, x => x.Name == p);
            var endP = ClosestParent(endEl, x => x.Name == p);

            var startR = startEl.Parent;
            var endR = endEl.Parent;
            //test only
            //if (startP.Value.Contains("– Process Model") && textToReplace.Contains("Corporate Governance"))
            //{
            //    //Corporate Governance
            //    int eeii = 12;
            //}

            //select starting and ending part of text
            var startingText = startValuePosition < start ? startEl.Value.Substring(0, start - startValuePosition) : "";
            var endingText = end < endValuePosition + endEl.Value.Length ? endEl.Value.Substring(end - endValuePosition) : "";

            if(!string.IsNullOrEmpty(highlightColor))
            {
                //highlightcolor
                var firstRpREl = startR.Descendants().Where(x => x.Name.LocalName == "rPr").FirstOrDefault();
                if(firstRpREl != null)
                {
                    var highLightEl = firstRpREl.Elements().Where(x => x.Name.LocalName == "highlight").FirstOrDefault();

                    if(highLightEl != null)
                    {
                        highLightEl.Attribute(val).Value = highlightColor;
                    }
                    else
                    {
                        firstRpREl.AddFirst(XElement.Parse("<w:highlight xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" w:val=\"" + highlightColor + "\"/>"));
                    }
                }
                else
                {
                    var rprEl = XElement.Parse("<w:rPr xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\"><w:highlight w:val=\"" + highlightColor + "\"/></w:rPr>");
                    startR.AddFirst(rprEl);
                }
            }

            string originalString = element.Value.Substring(start, end - start);

            if (!isMultipleLines)
            {
                //set text into first T section
                startEl.Value = startingText + textToReplace;

           
                if (endEl != startEl)
                    endEl.Value = endingText;
                else
                {
                    startEl.Value += endingText;
                }


                //if text is in more p secions - move to first section))
                if (startP != endP)
                {
                    //delete all r from start r
                    //delete all r to end r
                    startR.ElementsAfterSelf().ToList().ForEach(x => x.Remove());
                    endR.ElementsBeforeSelf().ToList().ForEach(x => x.Remove());

                    //remove p sections))))
                    if (!dontRemoveP)
                    {
                        ElementsAfterSelf(startP, endP).ForEach(x => x.Remove());

                        if (string.IsNullOrEmpty(endP.Value))
                        {
                            endP.Remove();
                        }
                    }
                }
                else
                {
                    if (startR != endR)
                    {
                        //remove all runs between startel and endel
                        List<XElement> innerRuns = ElementsAfterSelf(startR, endR);

                        innerRuns.ForEach(x => x.Remove());
                    }

                    //if (!dontRemoveP && string.IsNullOrEmpty(startP.Value))
                    //{
                    //    startP.Remove();
                    //}
                }
            }
            else
            {
                startEl.Value = startingText + textToReplace;


                if (endEl != startEl)
                    endEl.Value = endingText;
                else
                {
                    startEl.Value += endingText;
                }
                //test only to remove command
                
                
                var lines = textToReplace.Split(new char[] {'\n'});
                //clone starting p section , and remove all r elemens after text
                var newPs = new List<XElement>();

                var newP = XElement.Parse(startP.ToString());
                newP.Elements().Remove();

                foreach (var elBefore in startR.ElementsBeforeSelf().Where(x => x.Descendants().Where(y => !string.IsNullOrEmpty(y.Value)).Count() == 0))
                {
                    newP.AddFirst(elBefore);
                }

                if (startR != endR)
                {
                    //remove all runs between startel and endel
                    List<XElement> innerRuns = ElementsAfterSelf(startR, endR);

                    innerRuns.ForEach(x => x.Remove());
                }

                if (textToReplace.Contains("Operational service estimates (with support for Monitoring & Budget Cell)"))
                {
                    int ee = 12;
                }

                var clonedStartR = XElement.Parse(startR.ToString());
                //clonedStartR.Descendants().Where(x=>x.Name == t && x.Value.Length != 0).ToList().ForEach(x=>x.Value = "");
                newP.Add(clonedStartR);

                //remove elements from start p, all elements after text
                startR.ElementsAfterSelf().Remove();


               
                for (int indexLine = 0; indexLine < lines.Length; indexLine++)
                {
                    var curLine = lines[indexLine];
                    var clonedNewP = XElement.Parse(newP.ToString());

                    var textEl = clonedNewP.Descendants().Where(x => x.Elements().Count() == 0 && !string.IsNullOrEmpty(x.Value)).First();
                    textEl.Value = "";

                    if(indexLine == 0)
                    {
                        textEl.Value = "";
                        startEl.Value = startingText + curLine;// +"dalsi riadok";
                    }

                    if (indexLine != 0 && (indexLine + 1) != lines.Length)
                    {
                        textEl.Value = curLine;

                        newPs.Add(clonedNewP);
                    }

                    if ((indexLine + 1) == lines.Length)
                    {
                        //remove text element
                        //textEl.Remove();
                        //append endind text into last p element
                     //   clonedNewP.Add(endR);

                        clonedNewP.Add(endR.ElementsAfterSelf());

                        if (endEl != startEl)
                            textEl.Value = curLine + endingText;
                        else
                            textEl.Value += curLine + endingText;

                        newPs.Add(clonedNewP);
                    }

                   
                }

                //remove text after startEL
                //add text after startEl into lastonePsection

                //add new ps

                XElement lastClonedP = null;

                foreach (var curP in newPs)
                {
                    if(lastClonedP == null)
                    {
                        lastClonedP = curP;
                        startP.AddAfterSelf(curP);
                    }
                    else
                    {
                        lastClonedP.AddAfterSelf(curP);
                        
                        lastClonedP = curP;
                    }
                }
            }

            //set xml:space="preserve" on all text if contains "backspace"
            foreach (var preserveEl in startP.Descendants().Where(x => x.Name == t && x.Value.Contains(" ") && x.Attribute(xml_space) == null).ToList())
            {
                //if (preserveEl.Value.Contains(" - "))
                //{
                //    var iiu = 12;
                //}

                preserveEl.Add(new XAttribute(xml_space, "preserve"));

            }
        }

        public static XDocument CreateTemporaryWord()
        {
            return XDocument.Parse(@"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>
<w:document xmlns:ve=""http://schemas.openxmlformats.org/markup-compatibility/2006"" xmlns:o=""urn:schemas-microsoft-com:office:office"" xmlns:r=""http://schemas.openxmlformats.org/officeDocument/2006/relationships"" xmlns:m=""http://schemas.openxmlformats.org/officeDocument/2006/math"" xmlns:v=""urn:schemas-microsoft-com:vml"" xmlns:wp=""http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"" xmlns:w10=""urn:schemas-microsoft-com:office:word"" xmlns:w=""http://schemas.openxmlformats.org/wordprocessingml/2006/main"" xmlns:wne=""http://schemas.microsoft.com/office/word/2006/wordml"">
   
</w:document>
");
        }

        ImagePart AddImagePart(string imageName)
        {
            var existingPart = ImageParts.Where(x => x.ImageName == imageName).SingleOrDefault();

            if (existingPart != null)
                return existingPart;

            var testName = imageName.Replace(".bmp", ".jpg");
            existingPart = ImageParts.Where(x => x.ImageName == testName).SingleOrDefault();

            if (existingPart != null)
                return existingPart;

            //create new image part
            ImagePart ip = new ImagePart();

            FileInfo fi = new FileInfo(imageName);

            string fileName = fi.Name.Substring(0, fi.Name.Length - fi.Extension.Length);
            string extension = fi.Extension;

            var packageExtension = "jpeg";
            ip.ImageType = ImageTypes.jpg;

            if (extension.ToLower() == "xx.emf")//uncomment to do emf
            {
                packageExtension = "emf";
                ip.ImageType = ImageTypes.wmf;
            }



            Uri partUriDocument = PackUriHelper.CreatePartUri(
                             new Uri("/word/document.xml", UriKind.Relative));

            Uri partUriImage = PackUriHelper.CreatePartUri(
                         new Uri("/word/media/" + fileName + "." + packageExtension + "", UriKind.Relative));

            PackagePart packagePartResource = null;

            if (ip.ImageType == ImageTypes.jpg)
            {
                

                //var filepath = Path.Combine(Path.Combine(this.ReportSettings.ExportPath,"images"), imageName);

                if (!ReportManager.Provider.ImageExists(this.ReportSettings.ProjectName,imageName))
                {
                    //load converted image if bmp allready does not exists
                    imageName = imageName.Replace(".bmp", ".jpg");
                }

                if (!ReportManager.Provider.ImageExists(this.ReportSettings.ProjectName,imageName))
                {
                  //image does not exists
                    //log warnings
                    return null;
                }

                packagePartResource = Package.CreatePart(partUriImage, MediaTypeNames.Image.Jpeg);

                //convert to jpeg
                using (Image img = Image.FromStream(ReportManager.Provider.GetImage(this.ReportSettings.ProjectName,imageName)))
                {
                    //img.Save.Save("foo.jpg", ImageFormat.Jpeg);
                    img.Save(packagePartResource.GetStream(), ImageFormat.Jpeg);

                    //set image sizes
                    ip.X = img.Size.Width;
                    ip.Y = img.Size.Height;
                }
            }

            if (ip.ImageType == ImageTypes.wmf)
            {
                packagePartResource = Package.CreatePart(partUriImage, "image/x-emf");
               // packagePartResource = Package.CreatePart(partUriImage, MediaTypeNames.Image.Jpeg);

                //var filepath = Path.Combine(Path.Combine(this.ReportSettings.ExportPath, "images"), imageName);
                //convert to jpeg
                using (Image img = Image.FromStream(ReportManager.Provider.GetImage(this.ReportSettings.ProjectName,imageName)))
                {
                  //  img.Save(packagePartResource.GetStream(), ImageFormat.Emf);
                  //  img.Save(packagePartResource.GetStream(), ImageFormat.Jpeg);

                    //set image sizes
                    ip.X = img.Size.Width;
                    ip.Y = img.Size.Height;
                }

                using (var input = ReportManager.Provider.GetImage(this.ReportSettings.ProjectName, imageName))
                {
                    using (StreamReader reader = new StreamReader(input))
                    using (StreamWriter writer = new StreamWriter(packagePartResource.GetStream()))
                    {
                        writer.Write(reader.ReadToEnd());

                    }
                }
            }

            

           

            //create reference

            var packagePartDocument = Package.GetPart(partUriDocument);

            var relation = packagePartDocument.CreateRelationship(
                            partUriImage,
                            TargetMode.Internal,
                            "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image");

            ip.ID = relation.Id;
            ip.ImageName = imageName;
            ip.Uri = partUriImage.ToString();

            ImageParts.Add(ip);

            ImagesCount++;
            //XmlCIF.ProgressStep = "Image number " + ImagesCount + " imported";

            return ip;
        }

        public Package Package;
        public ReportSettingsArgs ReportSettings;

        public int ImagesCount;

        public class ImagePart
        {
            public string ID { get; set; }
            public string Uri { get; set; }
            public string ImageName { get; set; }

            public ImageTypes ImageType { get; set; }



            public int X { get; set; }
            public int Y { get; set; }
        }

        public enum ImageTypes
        {
            none = 0,
            bmp,
            jpg,
            wmf
        }

        #region WORD Namespaces

        public static readonly XName val = XName.Get("val", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
        public static readonly XName p = XName.Get("p", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
        public static readonly XName r = XName.Get("r", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
        public static readonly XName t = XName.Get("t", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
        public static readonly XName tc = XName.Get("tc", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
        public static readonly XName vMerge = XName.Get("vMerge", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
        
        //xml="http://www.w3.org/XML/1998/namespace
        public static readonly XName xml_space = XName.Get("space", "http://www.w3.org/XML/1998/namespace");
        

        public static readonly XName rsidR = XName.Get("rsidR", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
        public static readonly XName rsidP = XName.Get("rsidP", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
        public static readonly XName rsidRPr = XName.Get("rsidRPr", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");

        public static readonly XName proofErr = XName.Get("proofErr", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");

        public static readonly XName blip = XName.Get("blip", "http://schemas.openxmlformats.org/drawingml/2006/main");
        public static readonly XName attribute_embed = XName.Get("embed", "http://schemas.openxmlformats.org/officeDocument/2006/relationships");

        public static readonly XName tr = XName.Get("tr", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");

        #endregion
    }
}
